"""
    RFIDProtocol - Serial protocol for the CY8C0105 RFID ChipModule
    Copyright (C) 2004 by Andrew Hoffmann and Matthew Tan Creti
    The University of Iowa

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
"""


import serial
import time

ACK = 'acknwlge'
OK = 'acknokok'

class ProtocolError(Exception):
   "The protocol was not followed. Communication failed."

def read(ser, z, t="0", ww="64", x="\x02", y="\x01"):
#
# The "read" method will read z number of bytes for you
#
# Paramaters: ser, a valid serial port, and z, the number of bytes to be read
# Returns: a list of the values read
#
# Z should be between 1 and 7 for the cards used
#
# Other variables:
# t = read method (string values "0", "1", "2" or "3")
# ww = modulation type (string values "32" or "64")
# x = power mode (hex values "\x01" or "\x02"
# y = number of reads (hex values "\x01" through "\x05" correspond to number of times 1-5,
#			value "\x06" will read 10 times and "\x07" is "read always")
#
# Default values for t, ww, x and y will work correctly with the reader and cards
#
# This method will block until a card is passed over the reader

 try:
  zee = {
   1: "\x01",
   2: "\x02",
   3: "\x03",
   4: "\x04",
   5: "\x05",
   6: "\x06",
   7: "\x07"
  } [z]
 except KeyError:
  raise ProtocolError


 command = "re" + t + ww + x + y + zee
 ser.write(command)
 response = ser.read(8)
 if response != command: raise ProtocolError

 ser.write("acknwlge")
 response = ser.read(8)
 if response != "acknwlge": raise ProtocolError

 ser.write("acknokok")
 response = ser.read(8)
 if response != "acknokok": raise ProtocolError

 availible = 0
 while(availible == 0):
  availible = ser.inWaiting()	# Waits until a card is passed over the reader

 returnlist = []
 for x in range(z):
  response = ser.read(8)
  returnlist.append(response)

 return returnlist




def write_tag_low(ser,x,v1,v2,v3,v4,z=0):
#
# The "write_tag_low" method will write to a card in memory slots 1-7
# Writing to higher-numbered bytes is not possible on these cards and thus not implemented
# Note that writing to slot 1 while in byte-track method may require 
# a "byte_tracking" in order to read the card
# Writing to block zero would cause a lot of problems and is not allowed
#
# Parameters:
#  ser, a working serial port
#  x, the block to be programmed (must be 1-7)
#  v1-4, values to be written. Should be 4 bytes. Values longer than 4 bytes will only have
#     the first 8 bytes written. Non-hex values should be translated to hex first and will
# Returns:
#  none
#
# Other variables:
#  z, the lock option. Value of 0 leaves the block unlocked, 1 locks the block. Default is 0.
#  WARNING: LOCKING THE BLOCK WILL NOT ALLOW YOU TO WRITE TO THE BLOCK IN THE FUTURE
#
# Note: Card MUST be positioned over the reader WHEN THE COMMAND IS EXECUTED
#  in order for this to work correctly!

 try:
  xy = {
   1: "\x01" + "\x31",
   2: "\x02" + "\x32",
   3: "\x03" + "\x33",
   4: "\x04" + "\x34",
   5: "\x05" + "\x35",
   6: "\x06" + "\x36",
   7: "\x07" + "\x37"
  } [x] 
  zz = {
   0: "\x30" + "\x30",
   1: "\x31" + "\x31"
  } [z]
 except KeyError:
  print "KeyError - value for x or z not within range"
  raise ProtocolError
 
 command = "bl" + xy + "lc" + zz
 value = v1 + v2 + v3 + v4
 ser.write(command)
 response = ser.read(8)
# print response
 if response != command: 
  raise ProtocolError

 ser.write("acknwlge")
 response = ser.read(8)
# print response
 if response != "acknwlge": 
  raise ProtocolError

 ser.write("acknokok")
 response = ser.read(8)
# print response
 if response != "acknokok": 
  raise ProtocolError

 ser.write(value)
 response = ser.read(8)
# print response
 if response != value: 
  raise ProtocolError 

 ser.write("acknwlge")
 response = ser.read(8)
# print response
 if response != "acknwlge": raise ProtocolError

 ser.write("acknokok")
 response = ser.read(8)
 if response != "acknokok": raise ProtocolError



def byte_tracking(ser, value, x='\x04'):
# Byte tracking will read only the cards on which the value for block 1 matches the
# byte-track value. Can track bytes 1-4 of block 1.
#
# Parameters:
#  ser: working serial port
#  value: up to 4 bytes
#  x (hex): number of bytes to track
   command = 'trackda' + x
   ser.write(command)
   response = ser.read(8)
   if response != command: raise ProtocolError
   
   ser.write("acknwlge")
   response = ser.read(8)
   if response != "acknwlge": raise ProtocolError
   
   ser.write("acknokok")
   response = ser.read(8)
   if response != "acknokok": raise ProtocolError
   
   ser.write(value)
   response = ser.read(8) 
   if response != value: raise ProtocolError
  
   ser.write("acknwlge")
   response = ser.read(8)   
   if response != "acknwlge": raise ProtocolError
   
   ser.write("acknokok")
   response = ser.read(8)
   if response != "acknokok": raise ProtocolError
